package com.ljy.my_study.spring.tiny.study8.aop;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import org.aspectj.weaver.tools.PointcutExpression;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.PointcutPrimitive;
import org.aspectj.weaver.tools.ShadowMatch;

public class AspectJExpressPointcut implements Pointcut,ClassFilter,MethodMatcher{

	private String expression;
	
	private PointcutParser parser;
	
	private PointcutExpression pointcutExpression;
	
	private static final Set<PointcutPrimitive> DEFAULT_SUPPORTED_PRIMITIVES=new HashSet<>();
	
	static {
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
		DEFAULT_SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
	}
	
	public AspectJExpressPointcut() {
		this(DEFAULT_SUPPORTED_PRIMITIVES);
	}
	
	public AspectJExpressPointcut(Set<PointcutPrimitive> pointcutPrimitives) {
		parser=PointcutParser.
				getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
						pointcutPrimitives);
	}
	
	@Override
	public boolean matches(Method method, Class targetClass) {
		checkReadyToMatch();
		ShadowMatch match=pointcutExpression.matchesMethodExecution(method);
		if(match.alwaysMatches()) {
			return true;
		}else if(match.neverMatches()) {
			return false;
		}
		return false;
	}

	@Override
	public boolean matches(Class targetClass) {
		checkReadyToMatch();
		boolean result=pointcutExpression.couldMatchJoinPointsInType(targetClass);
		return result;
	}
	
	private void checkReadyToMatch() {
		if(pointcutExpression==null) {
			pointcutExpression=parser.parsePointcutExpression(expression);
		}
	}

	public void setExpression(String expression) {
		this.expression=expression;
	}
	
	@Override
	public ClassFilter getClassFilter() {
		return this;
	}

	@Override
	public MethodMatcher getMethodMatcher() {
		return this;
	}

}
